//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
#ifndef __CAMERADRIVER_H
#define __CAMERADRIVER_H

#include <windows.h>
#include <pm.h>
#include "hicamera_misc.h"
#include "Cs.h"
#include "Csmedia.h"
#include "camdll.h"
#include "campdd.h"

#define PIN_REG_PATH         TEXT("Software\\Microsoft\\DirectX\\DirectShow\\Capture")

#define dim(x)               (sizeof(x) / sizeof(x[0]))

#define SAFEDELETE( pointer ) \
    if ( NULL != pointer )    \
    {                         \
        delete pointer;       \
        pointer = NULL;       \
    }

#define MAX_SUPPORTED_PINS        2
#define MAX_PINNAME_LENGTH       10
#define MAX_VIDEO_FORMAT         12
#define NUM_VIDEOPROCAMP_ITEMS   10
#define NUM_CAMERACONTROL_ITEMS   8
#define NUM_PROPERTY_ITEMS       (NUM_VIDEOPROCAMP_ITEMS + NUM_CAMERACONTROL_ITEMS)

static const WCHAR g_wszPinNames[MAX_SUPPORTED_PINS][MAX_PINNAME_LENGTH] = { L"Capture"
                                                                            ,L"Still"
                                                                            };

static const WCHAR g_wszPinDeviceNames[MAX_SUPPORTED_PINS][MAX_PINNAME_LENGTH] = { L"PIN1:"
                                                                                  ,L"PIN1:"
};



typedef struct _Supported_Video_Formats
{
    GUID                categoryGUID;
    ULONG               ulAvailFormats;
    PCS_DATARANGE_VIDEO pCsDataRangeVideo[ MAX_VIDEO_FORMAT ];
} PINVIDEOFORMAT, * PPINVIDEOFORMAT;

typedef struct _Dev_Property 
{
    ULONG                     ulDefaultValue;
    ULONG                     ulCurrentValue;
    ULONG                     ulFlags;
    ULONG                     ulCapabilities;
    PCSPROPERTY_VALUES        pCsPropValues;
    PCSPROPERTY_STEPPING_LONG pRangeNStep;
    BOOL                      fGetSupported;
    BOOL                      fSetSupported;
} DEV_PROPERTY, * PDEV_PROPERTY;

typedef struct _Video_Control_Caps
{
    ULONG ulVideoControlCaps;
} VIDCONTROLCAPS, *PVIDCONTROLCAPS;

class CPinDevice;

typedef struct _StreamInstances
{
    ULONG        ulCInstances;
    CSSTATE      CsPrevState;
    CPinDevice * pPinDev;
} STREAM_INSTANCES, * PSTREAM_INSTANCES;

// CAutoLock helper class
class CAutoLock
{
public:
    CAutoLock (CRITICAL_SECTION * cs)
    {
        pcs = cs;
        EnterCriticalSection(pcs);
    }
    ~CAutoLock ()
    {
        LeaveCriticalSection(pcs);
    }
private:
    CRITICAL_SECTION * pcs;
};

typedef class CCameraDevice 
{

public:
    CCameraDevice( );

    ~CCameraDevice( );
    
    bool
    Initialize(
        PVOID context
        );
    BOOL InitProperty( );
    bool
    BindApplicationProc(
        HANDLE
        );

    bool
    UnBindApplicationProc( );
    
    DWORD
    AdapterHandlePinRequests(
        __in_bcount(InBufLen) PUCHAR pInBuf,
        DWORD  InBufLen,
        __out_bcount(OutBufLen) PUCHAR pOutBuf,
        DWORD  OutBufLen,
        PDWORD pBytesTransferred
        );

    DWORD
    AdapterHandleVersion(
        __out_bcount(OutBufLen) PUCHAR pOutBuf,
        DWORD  OutBufLen,
        PDWORD pBytesTransferred
        );

    DWORD
    AdapterHandleVidProcAmpRequests(
        __in_bcount(InBufLen) PUCHAR pInBuf,
        DWORD  InBufLen,
        __out_bcount(OutBufLen) PUCHAR pOutBuf,
        DWORD  OutBufLen,
        PDWORD pBytesTransferred 
        );

    DWORD
    AdapterHandleCamControlRequests(
        __in_bcount(InBufLen) PUCHAR pInBuf,
        DWORD  InBufLen,
        __out_bcount(OutBufLen) PUCHAR pOutBuf,
        DWORD  OutBufLen,
        PDWORD pBytesTransferred
        );

    DWORD
    AdapterHandleCompressionRequests(
        __in_bcount(InBufLen) PUCHAR pInBuf,
        DWORD  InBufLen,
        __out_bcount(OutBufLen) PUCHAR pOutBuf,
        DWORD  OutBufLen,
        PDWORD pBytesTransferred
        );

    DWORD
    AdapterHandleVideoControlRequests(
        __in_bcount(InBufLen) PUCHAR pInBuf,
        DWORD  InBufLen,
        __out_bcount(OutBufLen) PUCHAR pOutBuf,
        DWORD  OutBufLen,
        PDWORD pBytesTransferred
        );
    
    DWORD
    AdapterHandleDroppedFramesRequests(
        __in_bcount(InBufLen) PUCHAR pInBuf,
        DWORD  InBufLen,
        __out_bcount(OutBufLen) PUCHAR pOutBuf,
        DWORD  OutBufLen,
        PDWORD pBytesTransferred
        );
    
    LPVOID
    ValidateBuffer(
        __in_bcount(ulActualBufLen) LPVOID  lpBuff,
        ULONG   ulActualBufLen,
        ULONG   ulExpectedBuffLen,
        DWORD * Error
        );
    
    bool
    AdapterCompareFormat(
        const ULONG                 PinId,
        const PCS_DATARANGE_VIDEO   pCsDataRangeVideoToCompare,
        PCS_DATARANGE_VIDEO       * ppCsDataRangeVideoMatched,
        bool                        fDetailedComparison
        );

    bool
    AdapterCompareFormat(
        const ULONG                            PinId,
        const PCS_DATAFORMAT_VIDEOINFOHEADER   pCsDataRangeVideoToCompare,
        PCS_DATARANGE_VIDEO                  * ppCsDataRangeVideoMatched,
        bool                                   fDetailedComparison
        );

    bool
    IsValidPin(
        ULONG PinId
        );

    bool
    GetPinFormat(
        ULONG                 PinId,
        ULONG                 ulIndex,
        PCS_DATARANGE_VIDEO * ppCsDataRangeVid
        );

    bool
    IncrCInstances(
        ULONG        PinId,
        CPinDevice * pPinDev
        );

    bool
    DecrCInstances(
        ULONG PinId
        );

    bool
    UpdateDevicePowerState();

    bool
    PauseCaptureAndPreview( );

    bool
    RevertCaptureAndPreviewState( );

    // Camera PDD Specific function calls
    BOOL PDDPrepare  ( 
        ULONG PinId, 
        IRQ_CALLBACK lpThread, 
        LPVOID lpContext);

    BOOL PDDUnprepare( 
        ULONG PinId 
        );

    BOOL PDDRun  ( 
        ULONG PinId 
        );

    BOOL PDDPause( 
        ULONG PinId 
        );

    BOOL PDDStop ( 
        ULONG PinId 
        );

    BOOL PDDSetFormat( 
        ULONG PinId, 
        PCS_VIDEOINFOHEADER pCS
        );

    BOOL PDDPower( 
        CEDEVICE_POWER_STATE newPowerState
        );
    DWORD PDDMaxNumOfBuffers();
        
    BOOL PDDEnqueue(DWORD dwPhyAddr, DWORD dwVirtAddr);
    void PDDBufferFill( 
        ULONG PinId,
        PCS_VIDEOINFOHEADER pCsVideoInfoHdr,
        PCSSTREAM_HEADER pCsStreamHeader,
        PCS_FRAME_INFO   pCsFrameInfo,
        PVOID            pPDDContext
        );

    BOOL PDDSetVidProcAmp( 
        ULONG PropId, 
        LONG value,
        BOOL manual
        );

    BOOL PDDSetCameraControl( 
        ULONG PropId, 
        LONG value,
        BOOL manual
        );

    BOOL PDDSetVideoControlCaps(
        ULONG PinId,
        ULONG Caps
        );

    ULONG PDDGetVideoControlCaps(
        ULONG PinId
        );

    CRITICAL_SECTION *CSDeviceState() 
        { 
        return &m_csDeviceState; 
        }

    DWORD GetMemoryModel() 
        { 
        return m_MemoryModel; 
        }

    DWORD   MaxNumOfBuffers()
        {
        return m_MaxNumOfBuffers;
        }
private:
    void
    GetBasicSupportInfo(
        __out_bcount(OutBufLen) PUCHAR        pOutBuf,
        DWORD         OutBufLen,
        PDWORD        pBytesTransferred,
        PDEV_PROPERTY pDevProp,
        PDWORD        pError
        );

    void
    GetDefaultValues(
        __out_bcount(OutBufLen) PUCHAR        pOutBuf,
        DWORD         OutBufLen,
        PDWORD        pBytesTransferred,
        PDEV_PROPERTY pDevProp,
        PDWORD        pError
        );

    bool
    AdapterCompareGUIDsAndFormatSize(
        const PCSDATARANGE DataRange1,
        const PCSDATARANGE DataRange2
        );

public:
    // reg keys
    DWORD               m_MaxNumOfBuffers;
    DWORD               m_MemoryModel;

private:
    CRITICAL_SECTION    m_csDeviceState;                  // CS is also used by PIN and PDDD for device state changes 
    HANDLE              m_hStream;                        // Handle to the corresponding stream sub-device
    HANDLE              m_hCallerProcess;

    ULONG               m_CTypes;
    PINVIDEOFORMAT      m_PinVideoFormat[MAX_SUPPORTED_PINS];
    VIDCONTROLCAPS      m_PinVideoCaps  [MAX_SUPPORTED_PINS];
    STREAM_INSTANCES    m_StrmInstances [MAX_SUPPORTED_PINS];

    ULONG               m_PropSetSupported;               // Number of property item supported.
    CSPROPERTY_SET      m_VideoProcAmpSet;                // This is also the beginning of the property set table.
    CSPROPERTY_SET      m_CameraControlSet;

    //
    // Property items of what device supports 
    //
    CSPROPERTY_ITEM     m_VideoProcAmpItems[NUM_VIDEOPROCAMP_ITEMS];
    CSPROPERTY_ITEM     m_CameraControlItems[NUM_CAMERACONTROL_ITEMS];

    DEV_PROPERTY        m_DevProps[NUM_PROPERTY_ITEMS];

public:
    // Hardware interface to camera / data port pdds
    CCameraPort         *m_PDDINT;
    CCameraControl      *m_PDDCAM;
    CCameraSwitch       *m_PDDSwitch;
} CAMERADEVICE, * PCAMERADEVICE;

typedef struct CCameraOpenHandle
{
    PCAMERADEVICE pCamDevice;
} CAMERAOPENHANDLE, * PCAMERAOPENHANDLE;


// Camera PDD Specific function calls
inline BOOL 
CCameraDevice::PDDPrepare(
    ULONG PinId, 
    IRQ_CALLBACK lpThread, 
    LPVOID lpContext
    )
{
    BOOL bINT=m_PDDINT->Prepare( PinId, lpThread, lpContext);
    return bINT;
}

inline BOOL 
CCameraDevice::PDDUnprepare(
    ULONG PinId
    )
{
    BOOL bINT=m_PDDINT->Unprepare(PinId);
    return bINT;
}

inline BOOL 
CCameraDevice::PDDRun  (
    ULONG PinId
    )
{
    BOOL bINT;
    DWORD dwRetry=60;

    // the retry time should be longer than the DMA timeout interval
    do {
        bINT=m_PDDINT->Run(PinId);
        if (!bINT) Sleep(33);   // sleep 1/30s (one NTSC frame)
        if (dwRetry-- == 0) break;
    } while (!bINT);

    return bINT;
}

inline BOOL 
CCameraDevice::PDDPause(
    ULONG PinId
    )
{
    BOOL bINT=m_PDDINT->Pause(PinId);
    return bINT;
}

inline BOOL 
CCameraDevice::PDDStop (
    ULONG PinId
    )
{   
    BOOL bINT=m_PDDINT->Stop(PinId);
    return bINT;
}

inline BOOL 
CCameraDevice::PDDSetFormat( 
    ULONG PinId, 
    PCS_VIDEOINFOHEADER pCS
    )
{
    BOOL bINT=m_PDDINT->SetFormat(PinId, pCS);
    return bINT;
}

inline BOOL 
CCameraDevice::PDDPower( 
    CEDEVICE_POWER_STATE newPowerState
    )
{
    BOOL bINT=m_PDDINT->Power(newPowerState);
    return bINT;
}

inline void 
CCameraDevice::PDDBufferFill(
    ULONG PinId,
    PCS_VIDEOINFOHEADER pCsVideoInfoHdr,
    PCSSTREAM_HEADER pCsStreamHeader,
    PCS_FRAME_INFO   pCsFrameInfo,
    PVOID            pPDDContext
    )
{
    return m_PDDINT->BufferFill(
        PinId, 
        pCsVideoInfoHdr,
        pCsStreamHeader,
        pCsFrameInfo,
        pPDDContext
        );
}

inline DWORD 
CCameraDevice::PDDMaxNumOfBuffers(
    )
{
    return m_PDDINT->MaxNumOfBuffers( );
}

inline BOOL 
CCameraDevice::PDDEnqueue(DWORD dwPhyAddr, DWORD dwVirtAddr)
{
    return m_PDDINT->Enqueue(dwPhyAddr, dwVirtAddr);
}

inline BOOL
CCameraDevice::PDDSetVidProcAmp( 
    ULONG PropId, 
    LONG value,
    BOOL manual
    )
{

    BOOL bINT =  m_PDDCAM->SetVidProcAmp( PropId, value, manual);
    return bINT;    
}

inline BOOL
CCameraDevice::PDDSetCameraControl( 
    ULONG PropId, 
    LONG value,
    BOOL manual
    )
{
    BOOL bINT = m_PDDINT->SetCameraControl( PropId, value, manual);
    return bINT;
}

inline BOOL 
CCameraDevice::PDDSetVideoControlCaps(
    ULONG PinId,
    ULONG Caps
)
{
    BOOL bINT =  m_PDDINT->SetVideoControlCaps( PinId, Caps);
    return bINT;
}

inline ULONG 
CCameraDevice::PDDGetVideoControlCaps(
    ULONG PinId
    )
{
    BOOL bINT =  m_PDDCAM->GetVideoControlCaps( PinId );
    return bINT;
}

#endif // __CAMERADRIVER_H
